//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Vectorize raster map (semi-automatic).
// NOTE: the code is incomplete
//

# include "StdAfx.h"
# include "Vectorize.h"
# include "UndoRedo.h"
# include "Map.h"
# include "MapView.h"
# include "Position.h"
# include "Scale.h"
# include "Levels.h"
# include "CreateObject.h"
# include "fast_floor.h"
# include "ParseHelpers.h"
# include "Globals.h"
/*
float DColor (const BYTE * _pPoint1, const BYTE * _pPoint2) {
	const float fDRed   = _pPoint1 [0] - _pPoint2 [0];
	const float fDGreen = _pPoint1 [1] - _pPoint2 [1];
	const float fDBlue  = _pPoint1 [2] - _pPoint2 [2];
	return ::sqrt (fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue)/1.7f/255.f;
	//return (::fabs (fDRed) + ::fabs (fDGreen) + ::fabs (fDBlue))/3.f/255.f;
}

float DColor (COLORREF _col1, COLORREF _col2) {
	const float fDRed   = GetRValue (_col1) - GetRValue (_col2);
	const float fDGreen = GetGValue (_col1) - GetGValue (_col2);
	const float fDBlue  = GetBValue (_col1) - GetBValue (_col2);
	return ::sqrt (fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue)/1.7f/255.f;
	//return (::fabs (fDRed) + ::fabs (fDGreen) + ::fabs (fDBlue))/3.f/255.f;
}

COLORREF AveColor (const BYTE * _pBits, size_t _cScanWidth, size_t _cHeight, size_t _x, size_t _y) {
	float r = 0, g = 0, b = 0;

	const size_t cA = 1;
	for (size_t x = _x - cA; x <= _x + cA; ++ x)
		for (size_t y = _y - cA; y <= _y + cA; ++ y) {
			const BYTE * const pPoint = _pBits + x*3 + (_cHeight - 1 - y)*_cScanWidth;
			r += pPoint [0];
			g += pPoint [1];
			b += pPoint [2];
		}

	return RGB (r/(1 + cA*2), g/(1 + cA*2), b/(1 + cA*2));
}

float AveIntensity (const BYTE * _pBits, size_t _cScanWidth, size_t _cHeight, size_t _x, size_t _y) {
	float fRet = 0;

	const size_t cA = 2;
	for (size_t x = _x - cA; x <= _x + cA; ++ x)
		for (size_t y = _y - cA; y <= _y + cA; ++ y) {
			const BYTE * const pPoint = _pBits + x*3 + (_cHeight - 1 - y)*_cScanWidth;
			fRet += pPoint [0] + pPoint [1] + pPoint [2];
		}

	return fRet;
}
*/
struct rgb_t {
	BYTE b, g, r;
};
# define GET_POINT(_x,_y) reinterpret_cast<const rgb_t *> (_pBits + int(_x)*3 + (_cHeight - int(_y))*_cScanWidth)

//static
//void 

static
void _Vectorize (const BYTE * _pBits, size_t _cScanWidth, size_t _cWidth, size_t _cHeight, int _x, int _y, const rect_t & _rect, points_t & _points) {
	const int c_w = 5;

	// Get average color.
	int ra = 0, ga = 0, ba = 0;
	int xt;
	for (xt = _x - c_w; xt <= _x + c_w; ++ xt) {
		const rgb_t * pc = GET_POINT (xt, _y);
		ra += pc->r;
		ga += pc->g;
		ba += pc->b;
	}
	ra /= c_w*2 + 1;
	ga /= c_w*2 + 1;
	ba /= c_w*2 + 1;

	// Get the most different color point.
	int xt_best = _x;
	int d_max = 0;
	int r0 = ra;
	int g0 = ga;
	int b0 = ba;
	for (xt = _x - c_w; xt <= _x + c_w; ++ xt) {
		const rgb_t * pc1 = GET_POINT (xt - 1, _y);
		const rgb_t * pc2 = GET_POINT (xt,     _y);
		const rgb_t * pc3 = GET_POINT (xt + 1, _y);
		const int r = (pc1->r + pc3->r)/4 + pc2->r/2;
		const int g = (pc1->g + pc3->g)/4 + pc2->g/2;
		const int b = (pc1->b + pc3->b)/4 + pc2->b/2;

		const int d = ::abs (r - ra) + ::abs (g - ga) + ::abs (b - ba);
		if (d > d_max) {
			d_max = d;
			xt_best = xt;
			r0 = r;
			g0 = g;
			b0 = b;
		}
	}

	int x = xt_best, y = _y;
	float a = 0;

	for (size_t c = 0; c < 20; ++ c) {
		point_t point (
			_rect.x0 + x*_rect.Width  ()/_cWidth,
			_rect.y1 - y*_rect.Height ()/_cHeight
		);
		_points.push_back (point);

		int d_min = 255*3;
		float t_best = 0;
		const float R = 5;
		const float dt = 1/R;
		for (float t = -2; t <= 2; t += dt) {
			const int xt1 = x + fast_floor (R*::cos (a + t - dt) + .5f);
			const int xt2 = x + fast_floor (R*::cos (a + t     ) + .5f);
			const int xt3 = x + fast_floor (R*::cos (a + t + dt) + .5f);
			const int yt1 = y + fast_floor (R*::sin (a + t - dt) + .5f);
			const int yt2 = y + fast_floor (R*::sin (a + t     ) + .5f);
			const int yt3 = y + fast_floor (R*::sin (a + t + dt) + .5f);

			const rgb_t * pc1 = GET_POINT (xt1, yt1);
			const rgb_t * pc2 = GET_POINT (xt2, yt2);
			const rgb_t * pc3 = GET_POINT (xt3, yt3);
			const int r = (pc1->r + pc3->r)/4 + pc2->r/2;
			const int g = (pc1->g + pc3->g)/4 + pc2->g/2;
			const int b = (pc1->b + pc3->b)/4 + pc2->b/2;

			const int d = ::abs (r - r0) + ::abs (g - g0) + ::abs (b - b0);
			if (d < d_min) {
				d_min = d;
				t_best = t;
			}
		}

		const float dx = R*::cos (a + t_best);
		const float dy = R*::sin (a + t_best);
		x += fast_floor (dx + .5f);
		y += fast_floor (dy + .5f);
		a += t_best;
	}
}

void Vectorize (long _lX, long _lY, const image_t * _pImage) {
# ifndef _DEBUG
	ErrorMsgBox ("Sorry, the feature is not implemented.");
	return;
# endif // _DEBUG

	assert (_pImage);

	//
	// Get coordinates of the start point.
	//
	const rect_t & rect = _pImage->rect;
	const long lX0 = XToScreen (rect.x0);
	const long lX1 = XToScreen (rect.x1);
	const long lY0 = YToScreen (rect.y0);
	const long lY1 = YToScreen (rect.y1);
	const size_t cWidth  = _pImage->pBitmap->Width ();
	const size_t cHeight = _pImage->pBitmap->Height ();
	const size_t cImageX = cWidth*(_lX - lX0)/(lX1 - lX0);
	const size_t cImageY = cHeight*(_lY - lY1)/(lY0 - lY1);

	//
	// Access source bits to perform projection's transform.
	//
	BITMAPINFOHEADER bmih;
	::memset (& bmih, 0, sizeof (bmih));
	bmih.biSize = sizeof (bmih);
	bmih.biWidth = cWidth;
	bmih.biHeight = cHeight;
	bmih.biPlanes = 1;
	bmih.biBitCount = 24;
	bmih.biCompression = BI_RGB;
	bmih.biSizeImage = 0;
	bmih.biClrUsed = 0;
	bmih.biClrImportant = 0;

	const size_t cScanWidth = Align4 (3*cWidth);
	BYTE * const pBits = new BYTE [cScanWidth*cHeight];
	if (pBits == NULL)
		return;
	CByteArrayHolder pBits_Holder (pBits);
//	::GetDIBits (_pImage->pBitmap->GetDC (), _pImage->pBitmap->GetBitmap (), 0, cHeight, pBits, reinterpret_cast <BITMAPINFO *> (& bmih), DIB_RGB_COLORS);

	//
	//
	//

	points_t points;
	_Vectorize (pBits, cScanWidth, cWidth, cHeight, cImageX, cImageY, rect, points);

	CreateObject (points, rkPolyline, GetZoomLevel (g_fScale));
	Refresh ();
}
